#!/usr/bin/env python3
# -*-coding:utf8-*-
"""
XBox手柄指令读取演示程序
作者: Yinchuan Li
功能: 读取并显示XBox手柄的所有输入指令，包括摇杆、按钮、触发器、D-Pad等
版本: 1.0
日期: 2025-07-27

主要功能:
1. 实时读取手柄所有轴数据（摇杆、触发器）
2. 实时读取手柄所有按钮状态（A/B/X/Y、肩键、摇杆按下、D-Pad等）
3. 实时读取方向键状态（如果支持）
4. 显示手柄基本信息
5. 格式化输出便于观察

基于实际XBox手柄映射测试
"""

import pygame
import time

class XboxControllerReader:
    """
    XBox手柄读取器
    用于读取和显示手柄的所有输入数据
    """
    
    def __init__(self):
        """
        初始化手柄读取器
        """
        # 初始化Pygame
        pygame.init()
        pygame.joystick.init()
        
        # 手柄对象
        self.joystick = None
        
        # 数据存储
        self.axes_data = []  # 轴数据
        self.buttons_data = []  # 按钮数据
        self.hat_data = []  # 方向键数据
        
        # 显示控制
        self.display_interval = 0.1  # 显示间隔（秒）
        self.last_display_time = 0
        
        # 初始化手柄
        self._initialize_joystick()
    
    def _initialize_joystick(self):
        """
        初始化手柄连接
        """
        print("正在初始化XBox手柄...")
        
        # 检查手柄数量
        joystick_count = pygame.joystick.get_count()
        print(f"检测到 {joystick_count} 个手柄设备")
        
        if joystick_count == 0:
            raise RuntimeError("未检测到手柄，请确保XBox手柄已连接")
        
        # 连接第一个手柄
        self.joystick = pygame.joystick.Joystick(0)
        self.joystick.init()
        
        # 获取手柄信息
        self._print_joystick_info()
        
        print("手柄初始化完成！")
    
    def _print_joystick_info(self):
        """
        打印手柄基本信息
        """
        print("\n=============== 手柄信息 ===============")
        print(f"手柄名称: {self.joystick.get_name()}")
        print(f"轴数量: {self.joystick.get_numaxes()}")
        print(f"按钮数量: {self.joystick.get_numbuttons()}")
        print(f"方向键数量: {self.joystick.get_numhats()}")
        print("=" * 40)
        
        # 显示轴信息
        print("\n轴信息:")
        for i in range(self.joystick.get_numaxes()):
            print(f"  轴 {i}: 范围 [-1.0, 1.0]")
        
        # 显示按钮信息
        print("\n按钮信息:")
        for i in range(self.joystick.get_numbuttons()):
            print(f"  按钮 {i}: 0=释放, 1=按下")
        
        # 显示方向键信息
        print("\n方向键信息:")
        for i in range(self.joystick.get_numhats()):
            print(f"  方向键 {i}: (x, y) 坐标")
    
    def _get_axis_names(self):
        """
        获取轴名称映射
        
        Returns:
            dict: 轴索引到名称的映射
        """
        # 基于实际测试的XBox手柄轴映射
        axis_names = {
            0: "左摇杆X",    # 左摇杆 X 轴
            1: "左摇杆Y",    # 左摇杆 Y 轴
            2: "左扳机",    # 右摇杆 X 轴
            3: "右摇杆X",    # 右摇杆 Y 轴
            4: "右摇杆Y",     # 左扳机 (LT)
            5: "右扳机"      # 右扳机 (RT)
        }
        return axis_names
    
    def _get_button_names(self):
        """
        获取按钮名称映射
        
        Returns:
            dict: 按钮索引到名称的映射
        """
        # 基于实际测试的XBox手柄按钮映射
        button_names = {
            0: "A",          # A按钮
            1: "B",          # B按钮
            2: "X",          # X按钮
            3: "Y",          # Y按钮
            4: "LB",         # 左肩键
            5: "RB",         # 右肩键
            6: "Back",       # Back按钮
            7: "Start",      # Start按钮
            8: "Xbox Logo",  # 左摇杆按下
            9: "左摇杆按下",  # 右摇杆按下
            10: "右摇杆按下",     # 左扳机按钮
            11: "Menu (菜单)"      # 右扳机按钮
        }
        return button_names
    
    def _get_hat_names(self):
        """
        获取方向键名称映射
        
        Returns:
            dict: 方向键索引到名称的映射
        """
        # XBox手柄方向键映射
        hat_names = {
            0: "D-Pad"       # 十字方向键
        }
        return hat_names
    
    def _read_all_inputs(self):
        """
        读取所有手柄输入数据
        """
        # 处理Pygame事件
        pygame.event.pump()
        
        # 读取轴数据
        self.axes_data = []
        for i in range(self.joystick.get_numaxes()):
            value = self.joystick.get_axis(i)
            self.axes_data.append(value)
        
        # 读取按钮数据
        self.buttons_data = []
        for i in range(self.joystick.get_numbuttons()):
            value = self.joystick.get_button(i)
            self.buttons_data.append(value)
        
        # 读取方向键数据
        self.hat_data = []
        for i in range(self.joystick.get_numhats()):
            value = self.joystick.get_hat(i)
            self.hat_data.append(value)
    
    def _format_axis_display(self):
        """
        格式化轴数据显示
        
        Returns:
            str: 格式化后的轴数据字符串
        """
        axis_names = self._get_axis_names()
        lines = ["轴数据:"]
        
        for i, value in enumerate(self.axes_data):
            name = axis_names.get(i, f"轴{i}")
            # 格式化数值，保留3位小数
            formatted_value = f"{value:6.3f}"
            
            # 根据轴类型添加不同的状态指示
            if i != 2 and i != 5:  # 摇杆轴 (0-3)
                if abs(value) < 0.1:
                    status = "  [中心]"
                elif value > 0.5:
                    status = "  [激活]"
                elif value < -0.5:
                    status = "  [激活]"
                else:
                    status = "  [轻微]"
            else:  # 扳机轴 (4-5)
                if value < 0.1:
                    status = "  [释放]"
                elif value > 0.8:
                    status = "  [完全按下]"
                else:
                    status = "  [部分按下]"
            
            lines.append(f"  {name:8}: {formatted_value}{status}")
        
        return "\n".join(lines)
    
    def _format_button_display(self):
        """
        格式化按钮数据显示
        
        Returns:
            str: 格式化后的按钮数据字符串
        """
        button_names = self._get_button_names()
        lines = ["按钮数据:"]
        
        # 每行显示4个按钮
        buttons_per_line = 4
        for i in range(0, len(self.buttons_data), buttons_per_line):
            line_buttons = []
            for j in range(buttons_per_line):
                idx = i + j
                if idx < len(self.buttons_data):
                    name = button_names.get(idx, f"按钮{idx}")
                    value = self.buttons_data[idx]
                    status = "按下" if value else "释放"
                    line_buttons.append(f"{name:8}: {status}")
            
            lines.append("  " + " | ".join(line_buttons))
        
        return "\n".join(lines)
    
    def _format_hat_display(self):
        """
        格式化方向键数据显示
        
        Returns:
            str: 格式化后的方向键数据字符串
        """
        hat_names = self._get_hat_names()
        lines = ["方向键数据:"]
        
        for i, value in enumerate(self.hat_data):
            name = hat_names.get(i, f"方向键{i}")
            x, y = value
            
            # 方向描述
            direction = []
            if y == 1:
                direction.append("上")
            elif y == -1:
                direction.append("下")
            if x == 1:
                direction.append("右")
            elif x == -1:
                direction.append("左")
            
            if not direction:
                direction = ["中心"]
            
            direction_str = "".join(direction)
            lines.append(f"  {name:8}: ({x:2d}, {y:2d}) [{direction_str}]")
        
        return "\n".join(lines)
    
    def _display_inputs(self):
        """
        显示所有输入数据
        """
        current_time = time.time()
        
        # 控制显示频率
        if current_time - self.last_display_time < self.display_interval:
            return
        
        self.last_display_time = current_time
        
        # 清屏（在支持的系统上）
        print("\033[2J\033[H", end="")
        
        # 显示标题
        print("=" * 60)
        print("XBox手柄输入数据实时监控")
        print(f"作者: Yinchuan Li | 时间: {time.strftime('%Y-%m-%d %H:%M:%S')}")
        print("=" * 60)
        
        # 显示轴数据
        print(self._format_axis_display())
        print()
        
        # 显示按钮数据
        print(self._format_button_display())
        print()
        
        # 显示方向键数据
        print(self._format_hat_display())
        print()
        
        # 显示操作提示
        print("操作提示:")
        print("  - 移动摇杆查看轴数据变化")
        print("  - 按下按钮查看按钮状态")
        print("  - 使用D-Pad方向键查看方向键数据")
        print("  - 按下扳机查看轴数据变化")
        print("  - 按 Ctrl+C 退出程序")
        print("-" * 60)
    
    def run(self):
        """
        运行手柄读取程序
        """
        print("开始读取手柄数据...")
        print("按 Ctrl+C 退出程序")
        
        try:
            while True:
                # 读取所有输入
                self._read_all_inputs()
                
                # 显示数据
                self._display_inputs()
                
                # 控制更新频率
                time.sleep(0.05)  # 20Hz更新频率
                
        except KeyboardInterrupt:
            print("\n程序被用户中断")
        except Exception as e:
            print(f"程序运行错误: {e}")
            import traceback
            traceback.print_exc()
        finally:
            self.cleanup()
    
    def cleanup(self):
        """
        清理资源
        """
        if self.joystick:
            self.joystick.quit()
        pygame.quit()
        print("程序已退出")

def main():
    """
    主函数 - 程序入口点
    """
    try:
        # 创建手柄读取器
        reader = XboxControllerReader()
        
        # 运行程序
        reader.run()
        
    except Exception as e:
        print(f"程序启动失败: {e}")
        import traceback
        traceback.print_exc()

if __name__ == "__main__":
    main()
